package com.dfch.auto.appium.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.time.Duration;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

import io.appium.java_client.MobileBy;
import io.appium.java_client.MobileElement;
import io.appium.java_client.TouchAction;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.AndroidElement;
import io.appium.java_client.touch.WaitOptions;
import io.appium.java_client.touch.offset.PointOption;

public class AndroidCMDUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(AndroidCMDUtil.class);

    String[] emotion = "_(┐「ε:)_ _(:3 」∠)_ (￣y▽￣)~*捂嘴偷笑 ・゜・(PД`q｡)・゜・ (ง •̀_•́)ง (•̀ᴗ•́)و ̑̑ ヽ(•̀ω•́ )ゝ (,,• ₃ •,,) (｡˘•ε•˘｡) (=ﾟωﾟ)ﾉ (○’ω’○) (´・ω・`) ヽ(･ω･｡)ﾉ (。-`ω´-) (´・ω・`) (´・ω・)ﾉ (ﾉ･ω･)ﾉﾞ (♥ó㉨ò)ﾉ♡ (ó㉨ò) ・㉨・ ( ・◇・)？ ヽ(*´Д｀*)ﾉ (´°̥̥̥̥̥̥̥̥ω°̥̥̥̥̥̥̥̥｀) (╭￣3￣)╭♡ (☆ﾟ∀ﾟ) ⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄. (´-ι_-｀) ಠ౪ಠ ಥ_ಥ (/≥▽≤/) ヾ(o◕∀◕)ﾉ ヾ(o◕∀◕)ﾉ ヾ(o◕∀◕)ﾉ *★,°*:.☆\\(￣▽￣)/$:*.°★* ヾ (o ° ω ° O ) ノ゙ ╰(*°▽°*)╯ (｡◕ˇ∀ˇ◕） o(*≧▽≦)ツ ≖‿≖✧ >ㅂ< ˋ▽ˊ \\(•ㅂ•)/♥ ✪ε✪ ✪υ✪ ✪ω✪ 눈_눈 ,,Ծ‸Ծ,, π__π （/TДT)/ ʅ（´◔౪◔）ʃ (｡☉౪ ⊙｡) o(*≧▽≦)ツ┏━┓拍桌狂笑 (●'◡'●)ﾉ♥ <(▰˘◡˘▰)> ｡◕‿◕｡ (｡・`ω´･) (♥◠‿◠)ﾉ ʅ(‾◡◝) (≖ ‿ ≖)✧ （´∀｀*) （＾∀＾） (o^∇^o)ﾉ ヾ(=^▽^=)ノ (*￣∇￣*) (*´∇｀*) (*ﾟ▽ﾟ*) (｡･ω･)ﾉﾞ (≡ω≡．) (｀･ω･´) (´･ω･｀) (●´ω｀●)φ (/= _ =)/~┴┴ (/\"≡ _ ≡)/~┴┴ (╯‵□′)╯︵┻━┻ (┙>∧<)┙へ┻┻ （╯－＿－）╯╧╧ (╬▔皿▔) (╬￣皿￣)凸 (｀･д･´) (ノ｀Д´)ノ (ﾟ皿ﾟﾒ) (＃｀皿´) ( -д-)ﾉ （°ο°） Σ( ° △ °|||)︴　 ∑(っ °Д °;)っ (°□°；) (⊙□⊙) ( ° ▽、° ) ⊙▂⊙ ミ ﾟДﾟ彡 ╮(╯▽╰)╭ ╮(╯_╰)╭ (￣▽￣\") （￣工￣lll） (﹁\"﹁) ( ＿ ＿)ノ｜扶墙 ㄟ( ▔, ▔ )ㄏ (*´Д｀*)".split(" ");


    private static final ConcurrentHashMap<String, MobileElement> ID_ELEMENT_CACHE = new ConcurrentHashMap<>();
    private static final ConcurrentHashMap<String, MobileElement> NAME_ELEMENT_CACHE = new ConcurrentHashMap<>();
    private static final ConcurrentHashMap<String, MobileElement> TEXT_ELEMENT_CACHE = new ConcurrentHashMap<>();
    private static final ConcurrentHashMap<String, MobileElement> CLASS_ELEMENT_CACHE = new ConcurrentHashMap<>();


    /**
     * @param driver     必要的安卓驱动器
     * @param selectType 元素选择方式 id\name\text\point
     * @param target     目标元素的选择器
     * @param action     操作指令
     * @param params     操作所需要的参数
     */
    public void execute(AndroidDriver<AndroidElement> driver, String action, String selectType, JSONArray target, JSONArray params) throws Exception {
        try {
            if (driver == null) {
                throw new RuntimeException("驱动获取，无法执行操作!");
            }

            if (StringUtils.isEmpty(action) || AndroidCMDEnum.valueOf(action) == null) {
                throw new RuntimeException("未指定操作,无法执行对应指令!");
            }


            AndroidCMDEnum actionEnum = AndroidCMDEnum.valueOf(action);

            Method method = actionEnum.getTargetMethod();
            if (method == null) {
                throw new RuntimeException("没有找到对应的操作:" + action);
            }
            int paramSize = method.getParameterCount();
            LOGGER.info("执行操作:{}", actionEnum.getDesc());
//        LOGGER.debug("调用方法：{}，参数列表:{},目标:{},传入参数:{}", method.getName(), JSON.toJSONString(method.getParameters()), JSON.toJSONString(target), JSON.toJSONString(params));

            MobileElement element = null;

            switch (actionEnum) {
                case input:
                    if (params == null || params.size() <= 0) {
                        throw new RuntimeException("指令操作配置不正确,不执行当前操作!");
                    }
                    String content = (String) params.get(0);
                    LOGGER.info("输入文本:{}", content);
                    int length = content.length();
                    Random r = new Random();
                    int insertEmotionIndex = r.nextInt(length);
                    content = content.substring(0, insertEmotionIndex) + emotion[r.nextInt(emotion.length)] + content.substring(insertEmotionIndex, content.length());
                    LOGGER.info("文本内容随机加入表情后:{}", content);
                    params.set(0, content);
                    switch (selectType) {
                        case "id":
                            if (ID_ELEMENT_CACHE.contains(target.get(0))) {
                                element = ID_ELEMENT_CACHE.get(target.get(0));
                            } else {
                                element = driver.findElement(MobileBy.id((String) target.get(0)));
                                ID_ELEMENT_CACHE.put(target.get(0).toString(), element);
                            }
                            break;

                        case "name":
                            if (NAME_ELEMENT_CACHE.contains(target.get(0))) {
                                element = NAME_ELEMENT_CACHE.get(target.get(0));
                            } else {
                                element = driver.findElement(MobileBy.name((String) target.get(0)));
                                NAME_ELEMENT_CACHE.put(target.get(0).toString(), element);
                            }
                            break;
                        case "text":
                            if (TEXT_ELEMENT_CACHE.contains(target.get(0))) {
                                element = TEXT_ELEMENT_CACHE.get(target.get(0));
                            } else {
                                element = driver.findElement(MobileBy.linkText((String) target.get(0)));
                                TEXT_ELEMENT_CACHE.put(target.get(0).toString(), element);
                            }
                            break;
                        case "class":
                            if (CLASS_ELEMENT_CACHE.contains(target.get(0))) {
                                element = CLASS_ELEMENT_CACHE.get(target.get(0));
                            } else {
                                element = driver.findElement(MobileBy.className((String) target.get(0)));
                                CLASS_ELEMENT_CACHE.put(target.get(0).toString(), element);
                            }
                            break;
                        default:
                            element = driver.findElement(MobileBy.id((String) target.get(0)));
                            LOGGER.warn("操作未正确指定选择器，默认使用ID选择器，可能导致脚本执行失败!");
                            break;
                    }

                    if (element == null) {
                        throw new RuntimeException("未找到对应控件:" + target + ",操作无法执行");
                    }
                    element.sendKeys(params.get(0) + "");
                    break;

                case click:
                case clear:
                    switch (selectType) {
                        case "id":
                            if (ID_ELEMENT_CACHE.contains(target.get(0).toString())) {
                                element = ID_ELEMENT_CACHE.get(target.get(0).toString());
                            } else {
                                element = driver.findElement(MobileBy.id((String) target.get(0)));
                                ID_ELEMENT_CACHE.put(target.get(0).toString(), element);
                            }
                            break;

                        case "name":
                            if (NAME_ELEMENT_CACHE.contains(target.get(0).toString())) {
                                element = NAME_ELEMENT_CACHE.get(target.get(0).toString());
                            } else {
                                element = driver.findElement(MobileBy.name((String) target.get(0)));
                                NAME_ELEMENT_CACHE.put(target.get(0).toString(), element);
                            }
                            break;
                        case "text":
                            if (TEXT_ELEMENT_CACHE.contains(target.get(0).toString())) {
                                element = TEXT_ELEMENT_CACHE.get(target.get(0).toString());
                            } else {
                                element = driver.findElement(MobileBy.linkText((String) target.get(0)));
                                TEXT_ELEMENT_CACHE.put(target.get(0).toString(), element);
                            }
                            break;
                        case "class":
                            if (CLASS_ELEMENT_CACHE.contains(target.get(0))) {
                                element = CLASS_ELEMENT_CACHE.get(target.get(0));
                            } else {
                                element = driver.findElement(MobileBy.className((String) target.get(0)));
                                CLASS_ELEMENT_CACHE.put(target.get(0).toString(), element);
                            }
                            break;
                        default:
                            element = driver.findElement(MobileBy.id((String) target.get(0)));
                            LOGGER.warn("操作未正确指定选择器，默认使用ID选择器，可能导致脚本执行失败!");
                            break;
                    }

                    if (element == null) {
                        throw new RuntimeException("未找到对应控件:" + target + ",操作无法执行");
                    }
                    if (params == null) {
                        method.invoke(element);
                    } else {
                        method.invoke(element, params.get(0));
                    }
                    break;
                case press:
                case tap:
                case longPress:
                case moveTo:
                    if (((params == null || params.size() <= 0) && paramSize == 0) || (paramSize > 0 && (params == null || params.size() != paramSize))) {
                        throw new RuntimeException("指令操作配置不正确,不执行当前操作!");
                    }
                    PointOption point = PointOption.point(Integer.parseInt(params.get(0) + ""), Integer.parseInt(params.get(1) + ""));
                    method.invoke(new TouchAction(driver), params.toArray());
                    break;
                case wait:
                case cancel:
                case perform:
                case release:
                    method.invoke(new TouchAction(driver));
                    break;
                case sleep:
                    Thread.sleep(Long.parseLong(params.get(0) + ""));
                    break;
                case swipeUp:
                case swipeDown:
                case swipeLeft:
                case swipeRight:
                    method.invoke(this, driver);
                    break;
            }
        } catch (Exception ex) {
            LOGGER.error("方法执行失败:{},{},{}", action, target, params);
            throw ex;
        }

    }


    /**
     * 向下滑动
     *
     * @param driver
     */
    public void swipeDown(AndroidDriver driver) {
        int width = driver.manage().window().getSize().width;
        int height = driver.manage().window().getSize().height;
        TouchAction action = new TouchAction(driver);
        action.press(PointOption.point(width / 2, height * 1 / 4));
        action.waitAction(WaitOptions.waitOptions(Duration.ofMillis(200)));
        action.moveTo(PointOption.point(width / 2, height * 4 / 5));
        action.release();
        action.perform();
    }


    /**
     * 向上滑动
     *
     * @param driver
     */
    public static void swipeUp(AndroidDriver driver) {
        int width = driver.manage().window().getSize().width;
        int height = driver.manage().window().getSize().height;
        TouchAction action = new TouchAction(driver);
        action.press(PointOption.point(width / 2, height * 4 / 5));
        action.waitAction(WaitOptions.waitOptions(Duration.ofMillis(200)));
        action.moveTo(PointOption.point(width / 2, height / 4));
        action.release();
        action.perform();
    }

    /**
     * 向左滑动
     *
     * @param driver
     */
    public static void swipeLeft(AndroidDriver driver) {
        int width = driver.manage().window().getSize().width;
        int height = driver.manage().window().getSize().height;
        TouchAction action = new TouchAction(driver);
        action.press(PointOption.point(((Double) (width * 0.75)).intValue(), height / 2));
        action.waitAction(WaitOptions.waitOptions(Duration.ofMillis(200)));
        action.moveTo(PointOption.point(((Double) (width * 0.05)).intValue(), height / 2));
        action.release();
        action.perform();
    }

    /**
     * 向右滑动
     *
     * @param driver
     */
    public static void swipeRight(AndroidDriver driver) {
        int width = driver.manage().window().getSize().width;
        int height = driver.manage().window().getSize().height;
        TouchAction action = new TouchAction(driver);
        action.press(PointOption.point(((Double) (width * 0.05)).intValue(), height / 2));
        action.waitAction(WaitOptions.waitOptions(Duration.ofMillis(200)));
        action.moveTo(PointOption.point(((Double) (width * 0.75)).intValue(), height / 2));
        action.release();
        action.perform();
    }
}
